package model.environment;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import model.Item;
import model.Sale;

public class Datamining {
	/**
	 * Returns a map of item IDs and a percent of how many times an item have been bought by a customer who also bought the specified item.
	 * @param itemID The item's ID.
	 * @return A map of item IDs and percents.
	 */
	private static Map<Integer, Float> getItemsBoughtWith(int itemID) {
		// FInd all customers who have bought the item with id itemID.
		List<Integer> customersWhoBought = getCustomersWhoBought(itemID);
		
		// Count what those customers have bought in our shop. Save in a HashMap<itemID, numberOfSales>.
		List<Sale> shopSales = Sales.getShopSales();
		Map<Integer, Integer> numberOfSales = new HashMap<Integer, Integer>();
		int sumSales = 0;
		
		for (Sale s : shopSales) {
			if (customersWhoBought.contains(s.getCustomerID())) {
				if (s.getItemID() == itemID)
					continue;
				
				sumSales++;
				int value = 1;
				
				if (numberOfSales.containsKey(s.getItemID())) {
					value += numberOfSales.get(s.getItemID());
				}
				
				numberOfSales.put(s.getItemID(), value);
			}
		}
		
		// Convert numberOFSales to percent.
		Map<Integer, Float> result = new HashMap<Integer, Float>();
		
		for (Entry<Integer, Integer> entry : numberOfSales.entrySet()) {
			int value = entry.getValue();
			float percent = (float)(value) / (float)(sumSales);
			
			result.put(entry.getKey(), percent);
		}
		
		return result;
	}
	
	/**
	 * Returns a list of customers (customer IDs) who have bought an item.
	 * @param itemID The item, the customers have bought.
	 * @return A list of customer IDs).
	 */
	private static List<Integer> getCustomersWhoBought(int itemID) {
		List<Integer> result = new ArrayList<Integer>();
		
		// Get sales for our shop.
		List<Sale> shopSales = Sales.getShopSales();
		
		// Filter by customers who also bought item with id itemID.
		for (Sale s : shopSales) {
			if (s.getItemID() == itemID) {
				if (!result.contains(s.getCustomerID())) {
					result.add(s.getCustomerID());
				}
			}
		}
		
		return result;
	}
	
	/**
	 * Returns the items which have been most frequently bought by customers who have also bought the specified id.
	 * @param itemID The item's ID.
	 * @param numberOfItems The number of items to return.
	 * @returnA A list of item IDs belonging.
	 */
	public static List<Integer> getItemsFrequentlyBoughtWith(int itemID, int numberOfItems) {
		Map<Integer, Float> allItemsBoughtWith = getItemsBoughtWith(itemID);
		
		List<Integer> result = new ArrayList<Integer>();
		
		// Get the first numberOfItems "top" keys (highest value).
		// Maybe not the best implementation but yeah..
		for (int i = 0; i < numberOfItems; i++) {
			int maxKey = 0;
			Float maxValue = 0F;
			
			for (Entry<Integer, Float> entry : allItemsBoughtWith.entrySet()) {
				if (entry.getValue() > maxValue) {
					maxKey = entry.getKey();
					maxValue = entry.getValue();
				}
			}
			
			if (maxKey != 0) {
				result.add(maxKey);
				allItemsBoughtWith.remove(maxKey);
			}
		}
		
		return result;
	}
	/**
	 * Returns all items in the order of what has been sold the most. Even those that hasn't been sold at all. 
	 * @return All items in the order of what has been sold the most. Even those that hasn't been sold at all.
	 */
	public static List<Integer> getItemsInMostSoldOrder()
	{
		Map<Integer, Integer> itemMap = new HashMap<Integer, Integer>();
		// We need to have all items in the list, this is the way to do that. 
		for (Item i : Items.getItems())
		{
			itemMap.put(i.getItemID(), 0);
		}
		// Now add the number of sales to all the items. 
		for (Sale s : Sales.getShopSales())
		{
			Integer value = itemMap.get(s.getItemID()) + 1;
			itemMap.put(s.getItemID(), value);
		}
		// Selection sort. 
		List<Integer> result = new ArrayList<Integer>();
		
		// Not actually using the i variable, but its just an good way to iterate over all items in the map. 
		int size = itemMap.size();
		for (int i = 0; i < size; i++)
		{
			int maxKey = -1;
			int maxValue = -1;
			
			for (Entry<Integer, Integer> entry : itemMap.entrySet()) {
				if (entry.getValue() > maxValue) {
					maxKey = entry.getKey();
					maxValue = entry.getValue();
				}
			}
			if (maxKey != -1) {
				result.add(maxKey);
				itemMap.remove(maxKey);
			}
		}
		return result;
	}
	
	
	
	/**
	 * Return the items that are most likely that the customer will buy, based on what others that have bought the same stuff as him has bought.
	 * Note: The method will return the number of items that the param numberOfItems implies, trying to make it fit in a number of different ways.   
	 * @param customerID The ID of the customer.
	 * @param numberOfItems The number of items to return
	 * @return A list if itemID's. 
	 */
	public static List<Integer> getItemsMostLikelyBoughtBy(int customerID, int numberOfItems)
	{
		// Getting the items that this customer has brought ever from this shop. 
		List<Sale> customerSalesAllShops = Sales.getCustomerSales(customerID);
		List<Integer> customerSales = new ArrayList<Integer>();
		for (Sale s : customerSalesAllShops)
		{
			if (s.getShopID() == 128 && !customerSales.contains(s.getItemID()))
			{
				customerSales.add(s.getItemID());
			}
		}
		
		// Getting a combined map of high likely it is that others bought other items. 
		Map<Integer, Float> itemsMap = new HashMap<Integer, Float>();
		for (Integer t : customerSales)
		{
			Map<Integer, Float> item = getItemsBoughtWith(t);
			for (Entry<Integer, Float> e : item.entrySet())
			{
				Float value = 0f;
				if (itemsMap.containsKey(e.getKey()))
					value = e.getValue() + itemsMap.get(e.getKey());
				else
					value = e.getValue();
				itemsMap.put(e.getKey(), value);
			}
		}
		
		// We can't return the full thing, so first i trim it down, by removing some or all of the items that the customer has already bought. 
		// Each time, we remove the item that is the least likely that others would buy. 
		for (int i = 0; i < customerSales.size(); i++)
		{
			if (itemsMap.size() <= numberOfItems)
				break;
			Float min = Float.MAX_VALUE;
			Integer minItem = 0;
			for (Entry<Integer, Float> e : itemsMap.entrySet())
			{
				if (e.getValue() < min && customerSales.contains(e.getKey()))
				{
					min = e.getValue();
					minItem = e.getKey();
				}
			}
			if (minItem != 0)
			{
				itemsMap.remove(minItem);
			}
			else
			{
				break;
			}
		}
		
		// With the remaining items, we perform a selectionsort, to determine who gets shown. 
		List<Integer> result = new ArrayList<Integer>();
		
		for (int i = 0; i < numberOfItems; i++) {
			if (itemsMap.size() == 0)
				break;
			int maxKey = 0;
			Float maxValue = 0F;
			
			for (Entry<Integer, Float> entry : itemsMap.entrySet()) {
				if (entry.getValue() > maxValue) {
					maxKey = entry.getKey();
					maxValue = entry.getValue();
				}
			}
			
			if (maxKey != 0) {
				result.add(maxKey);
				itemsMap.remove(maxKey);
			}
		}
		
		// If we do not have enough items, we will add the most sold items. 
		if (result.size() < numberOfItems)
		{
			ArrayList<Integer> mostSold = (ArrayList<Integer>)getItemsInMostSoldOrder();
			int missing = numberOfItems - result.size();
			// First we remove the ones that we already have in the results list. 
			int remove = mostSold.size() - missing;
			for (int i = 0; i < remove; i++)
			{
				@SuppressWarnings("unchecked")
				List<Integer> mostSoldClone = (List<Integer>) mostSold.clone();
				for (Integer i1 : mostSoldClone)
				{
					if (result.contains(i1))
					{
						mostSold.remove((Integer)i1);
					}
				}
			}
			
			// TODO: Possibly add a step in the middle, where the ones that the customer have already brought are removed. 
			
			// Now, if there are still to many, we just remove. 
			remove = mostSold.size() - missing;
			if (remove > 0)
			{
				for (int i = 0; i < remove; i++)
				{
					if (mostSold.size() == 0)
						break;
					mostSold.remove(mostSold.size() - 1);
				}
			}
			// Now we need to add the extras into the result list. 
			for (int i = 0; i < missing; i++)
			{
				if (i > mostSold.size())
					break;
				result.add(mostSold.get(i));
			}
		}
		
		return result;
	}
	
	public static void main(String[] args) {
		System.out.println("test");
		List<Integer> items = getItemsMostLikelyBoughtBy(26, 3);
		
		System.out.println(items);
	}
}
